var elementNextID = 0;
var editorDiv = $("#Editor_div");

var caretAnimation = null;

function animateCaret() {
	$('.caret').fadeToggle(400);
}

function startCaretAnimation()
{
    if(caretAnimation==null)
    {
        caretAnimation = setInterval(animateCaret,400);
    }
}

$(document).ready(startCaretAnimation);

function moveCaret(x,y)
{
    $(".caret").css("top",y);
    $(".caret").css("left",x);
}
    

function Element()
{
    this.id = elementNextID;
    elementNextID++;
    this.parent = null;
    this.draw = function()
    {
        $("#element"+this.id).html(this.toInnerHTML());
    }
    this.topRightPosition = function()
    {
        element = $("#element"+this.id);
        var x = element.position().left+element.width();
        var y = element.position().top;
        return {"x":x,"y":y};
    }
    this.topLeftPosition = function()
    {
        element = $("#element"+this.id);
        var x = element.position().left;
        var y = element.position().top;
        return {"x":x,"y":y};
    }
}

function SimpleElement()
{
    Element.call(this);
    this.canBeFollowedByCaret = true;
    this.captureCaretRightFromParent = function()
    {
        return "EndOfElement";
    }
    this.captureCaretLeftFromParent = function()
    {
        return "BeginningOfElement";
    }
}

function CharacterElement(character)
{
    SimpleElement.call(this);
    this.type = "CharacterElement";
    this.character = character;
    this.toHTML = function()
    {
        return "<span id=\"element"+this.id+"\">"+this.toInnerHTML()+"</span>";
    }
    this.toInnerHTML = function()
    {
        if(this.character==" ")
        {
            return "&nbsp;";
        }
        else
        {
            return $("<div>").text(this.character).html();
        }
    }
    // See http://stackoverflow.com/questions/2541616/how-to-escape-strip-special-characters-in-the-latex-document
    this.toLatex = function()
    {
        var c = this.character;
        if(c=="#"||c=="$"||c=="%"||c=="&"||c=="_"||c=="{"||c=="}"||c==" ")
        {
            return "\\"+c;
        }
        else if(c=="~"||c=="^")
        {
            return "\\"+c+"{}";
        }
        else if(c=="\\")
        {
            return "\\textbackslash{}";
        }
        return c;
    }
}

function CompositeElement()
{
    Element.call(this);
    this.children = new Array();
    this.insertChild = function(index,child)
    {
        this.children.splice(index,0,child);
        child.parent = this;
    }
    this.removeChild = function(index)
    {
        this.children[index].parent = null;
        this.children.splice(index,1);
    }
    this.toInnerHTML = function()
    {
        r = "";
        for(var i=0;i<this.children.length;i++)
        {
            r += this.children[i].toHTML();
        }
        return r;
    }
    this.toInnerLatex = function()
    {
        r = "";
        for(var i=0;i<this.children.length;i++)
        {
            r += this.children[i].toLatex();
        }
        return r;
    }
}

function WrapperElement()
{
    CompositeElement.call(this);
    this.childIndexWithCaret = null;
    this.moveCaretLeftIntoChild = function(index)
    {
        for(var i=index;i>=0;i--)
        {
            var currentChild = this.children[i];
            var r = currentChild.captureCaretLeftFromParent();
            if(r=="Success")
            {
                this.childIndexWithCaret = i;
                return "Success";
            }
        }
        this.childIndexWithCaret = null;
        return "BeginningOfElement";
    }
    this.moveCaretRightIntoChild = function(index)
    {
        for(var i=index;i<this.children.length;i++)
        {
            var currentChild = this.children[i];
            var r = currentChild.captureCaretRightFromParent();
            if(r=="Success")
            {
                this.childIndexWithCaret = i;
                return "Success";
            }
        }
        this.childIndexWithCaret = null;
        return "EndOfElement";
    }
    this.captureCaretLeftFromParent = function()
    {
        return this.moveCaretLeftIntoChild(this.children.length-1);
    }
    this.captureCaretRightFromParent = function()
    {
        return this.moveCaretRightIntoChild(0);
    }
    this.captureCaretLeftFromChild = function(index)
    {
        return this.moveCaretLeftIntoChild(index-1);
    }
    this.captureCaretRightFromChild = function(index)
    {
        return this.moveCaretRightIntoChild(index+1);
    }
    this.insertAtCaret = function(child)
    {
        this.children[this.childIndexWithCaret].insertAtCaret(child);
    }
    this.moveCaretLeft = function()
    {
        var currentChild = this.children[this.childIndexWithCaret];
        var r = currentChild.moveCaretLeft();
        if(r=="Success")
        {
            return "Success";
        }
        else if(r=="BeginningOfElement")
        {
            return this.captureCaretLeftFromChild(this.childIndexWithCaret);
        }
    }
    this.moveCaretRight = function()
    {
        var currentChild = this.children[this.childIndexWithCaret];
        var r = currentChild.moveCaretRight();
        if(r=="Success")
        {
            return "Success";
        }
        else if(r=="EndOfElement")
        {
            return this.captureCaretRightFromChild(this.childIndexWithCaret);
        }
    }
}
            

function ContainerElement()
{
    CompositeElement.call(this)
    this.containsCaret = false;
    this.caretPosition = null;
    this.childIndexWithCaret = null;
    this.setCaret = function(index)
    {
        this.containsCaret = true;
        this.caretPosition = index;
        this.childIndexWithCaret = null;
        if(index==0)
        {
            position = this.topLeftPosition();
            moveCaret(position.x,position.y);
        }
        else
        {
            position = this.children[index-1].topRightPosition();
            moveCaret(position.x,position.y);
        }
    }
    this.captureCaretLeftFromParent = function()
    {
        this.setCaret(this.children.length);
        return "Success";
    }
    this.captureCaretRightFromParent = function()
    {
        this.setCaret(0);
        return "Success";
    }
    this.captureCaretLeftFromChild = function(index)
    {
        this.setCaret(index);
        return "Success";
    }
    this.captureCaretRightFromChild = function(index)
    {
        this.setCaret(index+1);
        return "Success";
    }
    this.moveCaretLeftIntoChild = function(index)
    {
        var child = this.children[index];
        var r = child.captureCaretLeftFromParent();
        if(r=="Success")
        {
            this.containsCaret = false;
            this.childIndexWithCaret = index;
            return "Success";
        }
        else if(r=="BeginningOfElement")
        {
            return this.captureCaretLeftFromChild(index);
        }
    }
    this.moveCaretRightIntoChild = function(index)
    {
        var child = this.children[index];
        var r = child.captureCaretRightFromParent();
        if(r=="Success")
        {
            this.containsCaret = false;
            this.childIndexWithCaret = index;
            return "Success";
        }
        else if(r=="EndOfElement")
        {
            return this.captureCaretRightFromChild(index);
        }
    }
    this.insertAtCaret = function(child)
    {
        if(this.containsCaret)
        {
            this.insertChild(this.caretPosition,child);
            this.draw();
            this.setCaret(this.caretPosition+1);
            return "Success";
        }
        else
        {
            return this.children[this.childIndexWithCaret].insertAtCaret(child);
        }
    }
    this.moveCaretLeft = function()
    {
        if(this.containsCaret)
        {
            if(this.caretPosition==0)
            {
                return "BeginningOfElement";
            }
            else
            {
                return this.moveCaretLeftIntoChild(this.caretPosition-1);
            }
        }
        else
        {
            var child = this.children[this.childIndexWithCaret];
            var r = child.moveCaretLeft();
            if(r=="Success")
            {
                return "Success";
            }
            else if(r=="BeginningOfElement")
            {
                return this.captureCaretLeftFromChild(this.childIndexWithCaret);
            }
        }
    }
    this.moveCaretRight = function()
    {
        if(this.containsCaret)
        {
            if(this.caretPosition==this.children.length)
            {
                return "EndOfElement";
            }
            else
            {
                return this.moveCaretRightIntoChild(this.caretPosition);
            }
        }
        else
        {
            var child = this.children[this.childIndexWithCaret];
            var r = child.moveCaretRight();
            if(r=="Success")
            {
                return "Success";
            }
            else if(r=="EndOfElement")
            {
                return this.captureCaretRightFromChild(this.childIndexWithCaret);
            }
        }
    }
}

function ParagraphElement()
{
    ContainerElement.call(this);
    this.type = "ParagraphElement";
    this.toHTML = function()
    {
        r = "<p id=\"element"+this.id+"\">";
        r += this.toInnerHTML();
        if(this.children.length==0)
        {
            r += "&nbsp;";
        }
        r += "</p>";
        return r;
    }
    this.toLatex = function()
    {
        return this.toInnerLatex()+"\n\n";
    }
    this.backspace = function()
    {
        if(this.containsCaret)
        {
            if(this.caretPosition==0)
            {
                return "BeginningOfElement";
            }
            else
            {
                this.removeChild(this.caretPosition-1);
                this.draw();
                this.setCaret(this.caretPosition-1);
                return "Success";
            }
        }
        else
        {
            // to be completed
        }
    }
}

function DocumentElement()
{
    WrapperElement.call(this);
    this.type = "DocumentElement";
    this.toLatex = function()
    {
        return this.toInnerLatex();
    }
    this.draw = function()
    {
        editorDiv.html(this.toInnerHTML());
    }
    this._moveCaretLeft = this.moveCaretLeft;
    this.moveCaretLeft = function()
    {
        var r = this._moveCaretLeft();
        if(r=="Success")
        {
            return "Success";
        }
        else if(r=="BeginningOfElement")
        {
            return this.captureCaretRightFromParent();
        }
    }
    this._moveCaretRight = this.moveCaretRight;
    this.moveCaretRight = function()
    {
        var r = this._moveCaretRight();
        if(r=="Success")
        {
            return "Success";
        }
        else if(r=="EndOfElement")
        {
            return this.captureCaretLeftFromParent();
        }
    }
    this.enter = function()
    {
        var paragraph = this.children[this.childIndexWithCaret];
        if(paragraph.containsCaret)
        {
            var caretIndex = paragraph.caretPosition;
            var firstPart = paragraph.children.slice(0,caretIndex);
            var secondPart = paragraph.children.slice(caretIndex,paragraph.children.length);
            var firstParagraph = new ParagraphElement();
            firstParagraph.children = firstPart;
            var secondParagraph = new ParagraphElement();
            secondParagraph.children = secondPart;
            this.removeChild(this.childIndexWithCaret);
            this.insertChild(this.childIndexWithCaret,firstParagraph);
            this.childIndexWithCaret++;
            this.insertChild(this.childIndexWithCaret,secondParagraph);
            this.draw();
            secondParagraph.captureCaretRightFromParent();
            return "Success";
        }
        else
        {
            // to be completed
        }
    }
    this.backspace = function()
    {
        var paragraph = this.children[this.childIndexWithCaret];
        var r = paragraph.backspace();
        if(r=="Success")
        {
            return "Success";
        }
        else if(r=="BeginningOfElement")
        {
            if(this.childIndexWithCaret==0)
            {
                return "Success";
            }
            else
            {
                var previousParagraph = this.children[this.childIndexWithCaret-1];
                var previousParagraphLength = previousParagraph.children.length;
                var newChildren = previousParagraph.children.concat(paragraph.children);
                var newParagraph = new ParagraphElement();
                newParagraph.children = newChildren;
                this.removeChild(this.childIndexWithCaret);
                this.removeChild(this.childIndexWithCaret-1);
                this.childIndexWithCaret--;
                this.insertChild(this.childIndexWithCaret,newParagraph);
                this.draw();
                newParagraph.setCaret(previousParagraphLength);
                return "Success";
            }
        }
    }
}

var rootElement = new DocumentElement();

function initialize()
{
    firstParagraph = new ParagraphElement();
    firstParagraph.containsCaret = true;
    firstParagraph.caretPosition = 0;
    firstParagraph.childIndexWithCaret = null;
    rootElement.insertChild(0,firstParagraph);
    rootElement.childIndexWithCaret = 0;
    rootElement.draw();
}

initialize();

function keypress(e)
{
    if(e.ctrlKey || e.metaKey)
    {
        return;
    }
    var keyCode = e.which;
    if(keyCode==13 && !e.shiftKey)
    {
        rootElement.enter();
    }
    else if(keyCode==13 && e.shiftKey)
    {
        // shift+enter
        // to be completed
    }
    else if(keyCode==32)
    {
        rootElement.insertAtCaret(new CharacterElement(" "));
    }
    else
    {
        var c = String.fromCharCode(keyCode);
        rootElement.insertAtCaret(new CharacterElement(c));
    }
}

function keydown(e)
{
    var keyCode = e.which;
    if(keyCode==37)
    {
        rootElement.moveCaretLeft();
    }
    else if(keyCode==39)
    {
        rootElement.moveCaretRight();
    }
    else if(keyCode==8)
    {
        rootElement.backspace();
    }
}

editorDiv.keypress(keypress);
editorDiv.keydown(keydown);

function exportLatex(e)
{
    alert(rootElement.toLatex());
}

$("#exportLatexButton").click(exportLatex);